home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Precision Software Appli…tions Silver Collection 1
/
Precision Software Applications Silver Collection Volume One (PSM) (1993).iso
/
tutor
/
modula12.exe
/
CHAP5.TXT
< prev
next >
Wrap
Text File
|
1988-10-26
|
24KB
|
521 lines
Chapter 5 - Modula-2 Procedures
In order to define the procedure, we will need to lay
some groundwork in the form of a few definitions.
Program Heading - This is the easiest part since it is
only one line, at least it has been in all of our
programs up to this point. It is simply the MODULE
line, and it never needs to be any more involved than
it has been up to this point, except for one small
addition which we will cover in a later chapter.
Declaration Part - This is the part of the Modula-2 source
code in which all constants, variables, and other user
defined auxiliary operations are defined. In some of
the programs we have examined, there have been one or
more VAR declarations and in one case a constant was
declared. These are the only components of the
declaration part we have used up to this time. There
are actually four components in the declaration part,
and the procedures make up the fourth part. We will
cover the others in the next chapter.
Statement Part - This is the last part of any Modula-2
program, and it is what we have been calling the main
program. It always exists bounded by the reserved
words BEGIN and END just as it has in all of our
examples to this point.
It is very important that you grasp the above
definitions because we will be referring to them constantly
during this chapter, and throughout the remainder of this
tutorial. With that introduction, let us look at our first
Modula-2 program with a procedure in it. It will, in fact,
have three procedures.
WHAT IS A PROCEDURE?
A Procedure is a group of statements, either predefined
by the compiler writer, or defined by you, that can be
called upon to do a specific job. In this chapter we will
see how to write and use a procedure. During your
programming in the future, you will use many procedures. In
fact, you have already used some because the "WriteString",
"WriteLn", etc procedures you have been using are predefined
procedures.
Load and display the program named PROCED1.MOD for your
first look at a user defined procedure. In this program, we
have the usual header with one variable defined. Ignore the
header and move down to the main program beginning with line
26. We will come back to all of the statements prior to the
main program in a few minutes.
Page 29
Chapter 5 - Modula-2 Procedures
The main program is very easy to understand based on
all of your past experience with Modula-2. First we somehow
write a header (WriteHeader), then write a message out 8
times (WriteMessage), and finally we write an ending out
(WriteEnding). Notice that with the long names for the
parts, no comments are needed, the program is self
documenting. The only problem we have is, how does the
computer actually do the three steps we have asked for.
That is the purpose for the 3 procedures defined earlier
starting in lines 8, 14, and 20. Modula-2 requires that
nothing can be used until it has been defined, so the
procedures are required to be defined prior to the main
program. This may seem a bit backward to you if you are
experienced in some other languages like FORTRAN, BASIC, or
C, but it will make sense eventually.
HOW DO WE DEFINE A PROCEDURE?
We will begin with the PROCEDURE at line 8. First we
must use the reserved word PROCEDURE followed by the name we
have chosen for our procedure, in this case "WriteHeader"
which is required to follow all of the rules for naming an
identifier. Following the PROCEDURE line, we can include
more IMPORT lists, define variables, or any of several other
things. We will go into a complete definition of this part
of the program in the next chapter. I just wanted to
mention that other quantities could be inserted here. We
finally come to the procedure body which contains the actual
instructions we wish to execute in the procedure. In this
case, the procedure body is very simple, containing only a
"WriteString" and a "WriteLn" instruction, but it could have
been as complex as we needed to make it.
At the end of the procedure, we once again use the
reserved word END followed by the same name as we defined
for the procedure name. In the case of a procedure, the
final name is followed by a semicolon instead of a period.
Other than this small change, a procedure definition is
identical to that of the program itself.
When the main program comes to the "WriteHeader"
statement, it knows that it is not part of its standard list
of executable instructions, so it looks for the user defined
procedure by that name. When it finds it, it transfers
control of the program sequence to there, and begins
executing those instructions. When it executes all of the
instructions in the procedure, it finds the END statement of
the procedure and returns to the next statement in the main
program. When the main program finally runs out of things
to do, it finds the END statement and terminates.
Page 30
Chapter 5 - Modula-2 Procedures
As the program executes the FOR loop, it is required to
call the "WriteMessage" procedure 8 times, each time writing
its message on the monitor, and finally it finds and
executes the "WriteEnding" procedure. This should be very
straightforward and should pose no real problem for you to
understand. When you think you understand what it should
do, compile and run it to see if it does.
NOW FOR A PROCEDURE THAT USES SOME DATA
The last program was interesting to show you how a
procedure works but if you would like to see how to get some
data into the procedure, load and display the program named
PROCED2.MOD. We will once again go straight to the program
starting in line number 25. We immediately notice that the
program is nothing more than one big FOR loop which we go
through 3 times. Each time through the loop we call several
procedures, some that are system defined, and some that are
user defined. This time instead of the simple procedure
name, we have a variable in the parentheses behind the
variable name. In these procedures, we will take some data
with us to the procedures, when we call them, just like we
have been doing with the "WriteString" and "WriteInt"
procedures.
We will take some data to the procedure named
"PrintDataOut" where it will be printed. The procedure
"PrintDataOut" starting in line 9 also contains a pair of
parentheses with a variable named "Puppy" which is of type
INTEGER. This says that it is expecting a variable to be
passed to it from the calling program and it expects the
variable to be of type INTEGER. Back to the main program we
see, on line 28, that the program did make the call to the
procedure with a variable named "Thing" which is an INTEGER
type variable, so everything is fine. The procedure prefers
to call the variable passed to it "Puppy" but that is
perfectly acceptable, it is the same variable. The
procedure writes the value of "Puppy", which is really the
variable "Thing" in the main program, in a line with an
identifying string, then changes the value of "Puppy" before
returning to the main program.
Upon returning to the main program, we print out
another line with three separate parts (notice the indenting
and the way it makes the program more readable), then calls
the next procedure "PrintAndModify" which appears to do the
same thing as the last one. Indeed, studying the procedure
itself leads you to believe they are the same, except for
the fact that this one prefers to use the name "Cat" for a
variable name. There is one subtle difference in this
Page 31
Chapter 5 - Modula-2 Procedures
procedure, the reserved word VAR in the header, line 17.
CALL BY VALUE & CALL BY REFERENCE
In the first procedure, the word VAR was omitted. This
is a signal to the compiler that this procedure will not
actually receive the variable reference, instead it will
receive a local copy of the variable which it can use in
whatever way it needs to. When it is finished, however, it
can not return any changes in the variable to the main
program because it can only work with its copy of the
variable. This is therefore a one-way variable, it can only
pass data to the procedure. This is sometimes called a
"call by value" or a "value parameter" in literature about
Modula-2.
In the second procedure, the word VAR was included.
This signals the compiler that the variable in this
procedure is meant to be actually passed to the procedure,
and not just the value of the variable. The procedure can
use this variable in any way it desires, and since it has
access to the variable in the main program, it can alter it
if it so desires. This is therefore a two-way variable, it
can pass data from the main program to the procedure and
back again. This is sometimes called a "call by reference"
or a "variable parameter" in literature about Modula-2.
WHICH SHOULD BE USED?
It is up to you to decide which of the two parameter
passing schemes you should use for each application. The
"two-way" scheme seems to give the greatest flexibility, so
your first thought is to simply use it everywhere. But that
is not a good idea because it gives every procedure the
ability to corrupt your main program variables. In
addition, if you use a "call by value" in the procedure
definition, you have the ability to call the procedure with
a constant in that part of the call. A good example is
given in lines 12, 20, 30, 34, and 38 of the present
program. If "WriteInt" were defined with a "call by
reference", we could not use a constant here, but instead
would have to set up a variable, assign it the desired
value, then use the variable name instead of the 5. There
are other considerations but they are beyond the level of
our study at this point.
BACK TO THE PROGRAM ON DISPLAY, PROCED2
We have already mentioned that both of the procedures
modify their respective local variables, but due to the
difference in "call by value" in the first, and "call by
Page 32
Chapter 5 - Modula-2 Procedures
reference" in the second, only the second can actually get
the modified data back to the calling program. This is why
they are named the way they are. One other thing should be
mentioned. Since it is not good practice to modify the
variable used to control the FOR loop, (and downright
erroneous in many cases) we make a copy of it and call it
"Thing" for use in the calls to the procedures. Based on
all we have said above, you should be able to figure out
what the program will do, then compile and run it.
SEVERAL PARAMETERS PASSED AT ONCE
Load and display the program named PROCED3.MOD for an
example of a procedure definition with more than one
variable being passed to it. In this case four parameters
are passed to this procedure. Three of the parameters are
one-way and one is a two-way parameter. In this case we
simply add the three numbers and return it to the main
program. Good programming practice would dictate the
placement of the single "call by reference" by itself and
the others grouped together, but it is more important to
demonstrate to you that they can be in any order you desire.
This is a very straightforward example that should pose no
problem to you. Compile and run it.
SCOPE OF VARIABLES
Load and display the program PROCED4.MOD for a program
which can be used to define scope of variables or where
variables can be used in a program. The three variables
defined in lines 6, 7, and 8, are of course available in the
main program because they are defined prior to it. The two
variables defined in the procedure are available within the
procedure because that is where they are defined. However,
because the variable "Count" is defined both places, it is
two completely separate variables. The main program can
never use the variable "Count" defined in the procedure, and
the procedure can never use the variable "Count" defined in
the main program. They are two completely separate and
unique variables with no ties between them. This is useful
because when your programs grow, you can define a variable
in a procedure, use it in whatever way you wish, and not
have to worry that you are corrupting some other "global"
variable. The variables in the main program are called
"global variables" because they are available everywhere.
In addition to the above scope rules, the variable
named "Apple" in the procedure, is not available to the main
program. Since it is defined in the procedure it can only
be used in the procedure. The procedure effectively builds
a wall around the variable "Apple" and its own "Count" so
Page 33
Chapter 5 - Modula-2 Procedures
that neither is available outside of the procedure. We will
see in the next chapter that procedures can be "nested"
leading to further hiding of variables. This program is
intended to illustrate the scope of variables, and it would
be good for you to study it, then compile and run it.
A PROCEDURE CAN CALL ANOTHER PROCEDURE
Load and display the program named PROCED5.MOD for an
example of procedures that call other procedures. Study of
this program will reveal that procedure "Three" starting on
line 19 calls procedure "Two" which in turn calls procedure
"One". The main program calls all three, one at a time, and
the result is a succession of calls which should be rather
easy for you to follow. The general rule is, "any program
or procedure can call any other procedure that has been
previously defined, and is visible to it." (We will say
more about visibility later.) Study this program then
compile and run it.
A FUNCTION PROCEDURE
Load and display the program named FUNCTION.MOD for an
example of a "Function Procedure". This contains a
procedure very much like the ones we have seen so far with
one difference. In the procedure heading, line 6, there is
an added ": INTEGER" at the end of the argument list. This
is a signal to the system that this procedure is a "function
procedure" and it therefore returns a value to the calling
program in a way other than that provided for by parameter
references as we have used before. In fact, this program
returns a single data value that will be of type INTEGER.
In line 16 of the calling program, we find the call to
the procedure which looks like the others we have used
except that it is used in an assignment statement as though
it is an INTEGER type variable. This is exactly what it is
and when the call is completed, the "QuadOfSum(Dogs,Cats)"
will be replaced by the answer and then assigned to the
variable "Feet". The entire call can therefore be used
anyplace in a program where it is legal to use an INTEGER
type variable. This is therefore a single value return and
can be very useful in the right situation. In one of the
earlier program, we used the "sin" and "cos" function
procedures and this is exactly what they were.
One additional point must be made here. If a function
procedure does not require any parameters, the call to it
must include empty parentheses, and the definition of the
procedure must include empty parentheses also. This is by
definition of the Modula-2 language.
Page 34
Chapter 5 - Modula-2 Procedures
In the procedure, we had to do one thing slightly
different in order to get the return value and that was to
use the RETURN reserved word. Whenever we have completed
the desired calculations or whatever we need to do, we put
the result that is to be returned to the main program in the
parentheses following the RETURN and the procedure will
terminate, return to the calling program, and take the value
with it as the answer. Due to decision making, we may have
several RETURN statements in the procedure but only one will
be exercised with each call. It is an error to come to the
END statement of a function procedure since that would
constitute a return without the benefit of the RETURN
statement, and no value would be returned to the calling
program.
WHAT IS RECURSION?
Recursion is simply a procedure calling itself. If you
have never been introduced to recursion before, that
definition sounds too simple but that is exactly what it is.
You have probably seen a picture containing a picture of
itself. The picture in the picture also contains a picture
of itself, the end result being an infinity of pictures.
Load the file named RECURSON.MOD for an example of a program
with recursion.
In the main program, "Count" is set to 7 and the
procedure is called taking along "Count" as a parameter. In
the procedure, we display a line containing the value of the
variable, now called "Index", and decrement it. If the
variable is greater than zero, we call the same procedure
again, this time entering it with the value of 6. It would
be reasonably correct to think of the system as creating
another copy of the procedure for this call. The variable
"Index" would be reduced to 5, and another copy of the
procedure would be called. Finally, the variable would be
reduced to zero and the return path from procedure to
procedure would be taken until the main program would be
reached, where the program would terminate.
Rather than making a complete new copy of the procedure
for each recursive call, the same code would be run each
time through and all of the data would be stored away on the
"stack" each time through. You have no need to worry about
this because it is all taken care of for you by the system.
You simply call the same procedure as though it were any
other procedure and the system will worry about all of the
details except for one. It is up to you to see that there
is some mechanism by which the process will terminate. If
there were no decrementing statement in the procedure, this
Page 35
Chapter 5 - Modula-2 Procedures
program would never reach an end and the stack would
overflow, resulting in an error message and termination of
the program. It would be worth your time to remove the
decrementing statement and observe this, after you compile
and run it the way it is now.
Recursion can be very useful for those problems that
warrant its use. This example is a very stupid use of
recursion, but is an excellent method for giving an example
of a program with recursion that is simple and easy to
understand.
DIRECT AND INDIRECT RECURSION
This example uses direct recursion because the
procedure calls itself directly. It is also possible to use
indirect recursion where procedure "A" calls "B", "B" calls
"A", etc. Either method is available and useful depending
on the particular circumstances.
PROGRAMMING EXERCISES
1. Write a program to write your name, address, and phone
number on the monitor with each line in a different
procedure.
2. Add a statement to the procedure in RECURSON to display
the value of "Index" after the call to itself so you
can see the value increasing as the recurring calls are
returned to the next higher level.
3. Rewrite TEMPCONV from chapter 4 putting the centigrade
to farenheit formula in a function procedure.
Page 36